﻿Imports Aricie.DNN.UI.Controls
Imports Aricie.DNN.Modules.LuceneSearch.Business
Imports Lucene.Net.Search
Imports Lucene.Net.Index
Imports System.Linq
Imports Aricie.DNN.Modules.LuceneSearch.Settings
Imports DotNetNuke.Services.Localization
Imports Aricie.DNN.Settings


Namespace Aricie.DNN.Modules.LuceneSearch.UI

    Partial Public Class PrefixFilter
        Inherits LuceneFilterControlBase

        Public Property Fields As String

        Public Property FieldValue() As String
            Get
                Return tbPrefix.Text
            End Get
            Set(ByVal value As String)
                If value IsNot Nothing Then
                    tbPrefix.Text = value.TrimEnd("*"c)
                Else
                    tbPrefix.Text = Nothing
                End If

            End Set
        End Property


        Public Overrides Function GetFilter() As LuceneFilterQuery
            Dim toReturn As New LuceneFilterQuery
            Dim searchString As String = FieldValue.Trim.ToLower

            If QueryMode = UI.QueryMode.Filter Then
                Dim fieldFilter As New BooleanQuery
                Dim splitValues As String() = searchString.Split(New String() {" "}, StringSplitOptions.RemoveEmptyEntries)

                For Each Field As String In GetFieldNames()
                    Dim bq As New BooleanQuery()
                    For Each value As String In splitValues.Take(splitValues.Length - 1)
                        bq.Add(New TermQuery(New Term(Field, value)), Occur.MUST)
                    Next
                    Dim PQ = New PrefixQuery(New Term(Field, splitValues.Last()))
                    PQ.RewriteMethod = MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE
                    bq.Add(PQ, Occur.MUST)
                    fieldFilter.Add(bq, Occur.SHOULD)
                Next
                toReturn.FilterQuery = fieldFilter
            Else
                toReturn.SearchQuery = searchString + "*"
            End If

            Return toReturn
        End Function

        Public Overrides Function ParseFilter(ByVal objFilter As Business.LuceneFilterQuery) As Boolean
            If TypeOf (objFilter.FilterQuery) Is BooleanQuery AndAlso Me.QueryMode = UI.QueryMode.Filter Then
                ' on vérifie que la signature de ce filtre correspond à ce qu'on fait en général, à savoir
                ' SHOULD (MUST(Field1:value1) MUST(Field1:value2) MUST(Field1:starts like value3)) SHOULD (MUST(Field2:value1) MUST(Field2:value2) MUST(Field2:starts like value3))
                ' Par ailleurs on va vérifier qu'on a bien les bons champs
                Dim FieldsInThisQuery As New HashSet(Of String)
                Dim QueryText As String = String.Empty

                For Each c As BooleanClause In DirectCast(objFilter.FilterQuery, BooleanQuery).GetClauses()
                    Dim TQs As List(Of TermQuery) = Nothing
                    Dim PQ As PrefixQuery = Nothing

                    Dim AbortForLoop As Boolean = False

                    Dim InnerQuery As BooleanQuery = TryCast(c.Query, BooleanQuery)
                    If InnerQuery IsNot Nothing Then
                        Dim Clauses As BooleanClause() = InnerQuery.GetClauses()
                        If Not (Clauses.Take(Clauses.Count - 1).All(Function(InnerClause) TypeOf InnerClause.Query() Is TermQuery) AndAlso TypeOf Clauses.Last.Query() Is PrefixQuery) Then
                            Return False
                        End If
                        TQs = Clauses.Take(Clauses.Count - 1).Select(Function(ic) DirectCast(ic.Query(), TermQuery)).ToList()
                        PQ = DirectCast(Clauses.Last.Query(), PrefixQuery)
                    Else
                        PQ = TryCast(c.Query(), PrefixQuery)
                        TQs = New List(Of TermQuery)
                        If PQ Is Nothing Then
                            ' dernière possibilité, on a un seul champ et plusieurs termes
                            Dim Clauses As BooleanClause() = DirectCast(objFilter.FilterQuery, BooleanQuery).GetClauses()
                            If Not (Clauses.Take(Clauses.Count - 1).All(Function(InnerClause) TypeOf InnerClause.Query() Is TermQuery) AndAlso TypeOf Clauses.Last.Query() Is PrefixQuery) Then
                                Return False
                            End If
                            TQs = Clauses.Take(Clauses.Count - 1).Select(Function(ic) DirectCast(ic.Query(), TermQuery)).ToList()
                            PQ = DirectCast(Clauses.Last.Query(), PrefixQuery)
                            AbortForLoop = True
                        End If
                    End If

                    Dim Fields = TQs.Select(Function(tq) tq.Term().Field()).Distinct
                    If Fields.Count > 1 Then
                        Return False
                    End If
                    Dim PQField = PQ.Prefix().Field
                    If (Fields.Count > 0 AndAlso PQField <> Fields(0)) Then
                        Return False
                    End If
                    FieldsInThisQuery.Add(PQField)

                    Dim CurrentQueryText = TQs.Select(Function(tq) tq.Term().Text).ToList()
                    CurrentQueryText.Add(PQ.Prefix().Text)
                    Dim CurrentQueryPhrase = String.Join(" ", CurrentQueryText.ToArray())
                    If String.IsNullOrEmpty(QueryText) Then
                        QueryText = CurrentQueryPhrase
                    Else
                        If QueryText <> CurrentQueryPhrase Then
                            Return False
                        End If
                    End If

                    If AbortForLoop Then
                        Exit For
                    End If
                Next
                Dim FieldsInFilter As New HashSet(Of String)
                For Each fField In GetFieldNames()
                    FieldsInFilter.Add(fField)
                Next
                If Not FieldsInThisQuery.SetEquals(FieldsInFilter) Then
                    Return False
                End If

                ' il semble qu'on ait la bonne suite de queries pour analyser la requete, récupérons la valeur de ce filtre
                FieldValue = QueryText
                Return True
            End If

            If QueryMode = UI.QueryMode.Search AndAlso Not String.IsNullOrEmpty(objFilter.SearchQuery) Then
                Me.FieldValue = objFilter.SearchQuery
                Return True
            End If

            Return False
        End Function

        Public Overrides Property Enabled As Boolean
            Get
                Return tbPrefix.Enabled
            End Get
            Set(ByVal value As Boolean)
                tbPrefix.Enabled = value
            End Set
        End Property

        Public Overrides Function GetFieldNames() As System.Collections.Generic.IEnumerable(Of String)
            Dim FieldNames As IEnumerable(Of String) = PortalQueryingDriver.GetCurrent().GetSearchFields(ModuleID).Select(Of String)(Function(t) t.Id)
            If Not String.IsNullOrEmpty(Fields) Then
                FieldNames = Fields.Split(New String() {";"}, StringSplitOptions.RemoveEmptyEntries)
            End If
            Return FieldNames
        End Function

        Public Overrides Function HasFilter() As Boolean
            Return Not String.IsNullOrEmpty(FieldValue)
        End Function
    End Class
End Namespace